layout: post
title: “如何阅读混淆加密过的JavaScript代码”
categories: [开发技术, Web前端]
tags: [JavaScript,加密,混淆]


1 代码混淆

  • 删除注释和多于的空格换行等分隔符
  • 变量和函数名称替换为较短的a,b,c等
  • 压缩代码体积减少流量和提高页面加载性能
  • 降低代码可读性,在一定程度上保护代码、增加攻击难度、提高安全性

2 代码加密

  • 通过字符串分隔、替换、拼接等方式生成JavaScript源代码文本
  • 通过将代码转换为十进制、十六进制,进行编码等方式来进行加密
  • 通过eval和Function来执行生成的JavaScript源代码文本
  • 最终始终会生成能被JavaScript引擎解析的JavaScript代码
  • 最终始终会生成能被JavaScript引擎解析的JavaScript代码
  • 最终始终会生成能被JavaScript引擎解析的JavaScript代码
  • 重要的事情说三遍

3 解决办法

3.1 代码混淆

  • 直接通过各大浏览器的 “{}” 代码美化工具进行格式化
  • 通过各种在线的代码美化、格式化工具进行格式化

3.2 代码加密

  • 直接避开加密算法,在代码生成之后在浏览器调试工具中中进行获取
  • 研究下代码加密方法,然后在生成后浏览器调试工具中进行获取
  • 写解密方法,获取代码

4 实例

My97DatePicker 是一个老牌的日期时间选择器,简约而不简单。

但免费使用的源代码是经过压缩和加密过的。

文件结构清晰
My97DatePicker
—-WdatePicker.js //主文件
—-calendar.js //日期时间选择器封装
—-lang
——–zh-CN.js
——–en.js
——–zh-TW.js

简单打开文件查看:

  • WdatePicker中除了配置部分,均经过混淆
  • calendar.js 进行过加密,使用eval执行
  • 语言包未进行过压缩或者加密

使用浏览器打开demo页面,点击并选择一个日期,在调试工具中可以看到加载的这些资源。
使用调试工具中的代码美化工具可以将WdatePicker.js 进行格式美化,便于调试和阅读。
但在calendar的代码,只是一行eval代码,在Chrome和Firefox下无法查看详细代码。
但在IE8-11、Edge中可以通过开发人员工具查看资源动态代码中的 eval code并进行代码美化,。

使用状态如下:

  • Chrome中无法查看eval 代码;
  • Firefox Firebug插件能查看eval代码,无法进行代码格式化,可以拷出来在编辑器或者在线代码格式化工具中进行格式化;
  • IE开发人员工具中可以查看eval code并进行代码格式化;

拿到了eval code之后,基本的代码逻辑就有了,没有混淆过的代码。

先来看一段拿到的eval code:

if($cfg.eCont) {
    $dp = {
    };
    for(var p in $pdp) {
        if(typeof $pdp[p] == "object") {
            $dp[p] = {
            };
            for(var pp in $pdp[p]) {
                $dp[p][pp] = $pdp[p][pp];
            }
        } else {
            $dp[p] = $pdp[p];
        }
    }
} else {
    $dp = $pdp;
}
for(p in $cfg) {
    $dp[p] = $cfg[p];
}
var $c;

再来看一段eval 中函数的第一个参数:

'l($4o.44){$f={};1b(q p 4r $2s)l(6p $2s[p]=="6o"){$f[p]={};1b(q 4G 4r $2s[p])$f[p][4G]=$2s[p][4G]}t $f[p]=$2s[p]}t $f=$2s;1b(p 4r $4o)$f[p]=$4o[p];q $c;

大致可以看出,这里面就是做了一个字符串映射和替换:

if-->l
$dp-->$f;
for-->1b;
var-->q;
等等

然后再来看看这个加密函数:

eval(function(p, a, c, k, e, d) {
    e = function(c) {
        return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
    }
    ;
    if (!''.replace(/^/, String)) {
        while (c--)
            d[e(c)] = k[c] || e(c);
        k = [function(e) {
            return d[e]
        }
        ];
        e = function() {
            return '\\w+'
        }
        ;
        c = 1;
    }
    ;while (c--)
        if (k[c])
            p = p.replace(new RegExp('\\b' + e(c) + '\\b','g'), k[c]);
    return p;
}(codeString,62,575,keywordString.split('|'),0,{}));

参数对应:

codeString-->p   //这里就是加密过的代码
62---------->a   
575--------->c   //猜这里是一个执行的限度,大小跟关键字列表的大小相同
keywords---->k   //代码中的关键字列表
0----------->e   //会直接被重写,不论传什么值效果都一样,或者说这个值没有意义
{}---------->d   //存放临时结果的字典

再回过头去看代码,并除掉一些冗余代码之后:

eval(function(p, a, c, k, e, d) {
    //重写e, 不论传什么值进来结果都一样
    e = function(c) {
        return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
    }
    
    // 有值就取值,没有值就算一个值,构成一个完整的字典
    while (c--)
        d[e(c)] = k[c] || e(c);
        
    //把对应位置上的关键字替换回来    
    p = p.replace(new RegExp('\\b\\w+\\b','g'), function(e) {
        return d[e]
    });
    return p;
}(codeString,62,575,keywordString.split('|'),0,{}));    

还没解决的一个问题,这个62 是什么意思?

等我收拾好了Chrome和Firefox的调试工具看看再说,调试工具失效了。

5 参考文档

  1. 使用 estools 辅助反混淆 Javascript
  2. JS混淆还原
  3. JS混淆代码还原-JS反混淆:利用JS进行赋值实现
  4. JAVASCRIPT加密解密终级指南
  5. 如何去除My97DatePicker控件上右键弹出官网的链接—如何debug混淆过的代码
  6. My97DatePicker Demo
  7. My97DatePicker 官网
× 赞赏这个人~
打赏二维码